package com.gszy.auth.mvc;

import com.gszy.auth.utils.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author MeUChT
 */
public class ParamUtils {

    static Set<Class<?>> classSet = new HashSet<>();

    static {
        //准备数值类型的类对象
        classSet.add(Byte.class);
        classSet.add(Short.class);
        classSet.add(Integer.class);
        classSet.add(Long.class);

        classSet.add(Float.class);
        classSet.add(Double.class);

        classSet.add(Boolean.class);
        classSet.add(Character.class);
        classSet.add(String.class);

    }

    /**
     * 准备参数列表 并且执行方法 调用methodinvoke()
     * @param object 表示要执行方法的对象 controller实例对象
     * @param method 通过映像确定的执行的方法
     * @param request 请求对象中包含请求数据 作为执行方法的参数列表
     * @return
     */
    public static Object setParameter(Object object, Method method, HttpServletRequest request){
        Object result=null;
        //获取当前method对象需要哪些参数
        Parameter[] parameters=method.getParameters();
        Class<?>[] parameterTypes=method.getParameterTypes();
        int size = parameters.length;
        int num=0;
        //用来记录传参的顺序

        //读取request对象，获取前端传递的所有参数，把参数封装到Map
        Object[]list=new Object[size];
        //在调用method.invoke(object,list)实际的传参的参数列表
        Map<String,String>param=getParameter(request);

        try {
            //遍历参数列表，所有执行该方法需要的参数
            for (int i = 0; i < size; i++) {
                //先确定这个参数的Parameter对象和类型
                Class<?>parameterType=parameterTypes[i];
                Parameter parameter=parameters[i];
                if (classSet.contains(parameterType)){
                    //如果参数的类型是基本的数值类型
                    //从注解中拿到key值映射
                    String pkey = parameter.getAnnotation(RequestParam.class).value();
                    //根据这个key值再request参数列表中获取value
                    String value = param.get(pkey);
                    //将request列表中获取到的值(value,是String类型)转换成Integer Double....
                    Object obj=getInstance(parameterType,value);
                    list[num++]=obj;
                }else if (parameterType.equals(HttpServletRequest.class)){
                    list[num++]=request;
                }else {
                    //Java Bean
                    Object instance = parameterType.newInstance();
                    setInstanceParam(instance,parameterType,param);
                    list[num++]=instance;
                }
            }
            result = method.invoke(object, list);
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }
    /**
     *
     * @param instance
     * @param parameterType
     * @param param
     */
    private static void setInstanceParam(Object instance, Class<?> parameterType, Map<String, String> param) {
        Method[] methods = parameterType.getMethods();
        for (Method method : methods){
            String methodName = method.getName();
            if (methodName.startsWith("set")||methodName.startsWith("is")){
                String setParameterName = StringUtils.toLowerCaseFirstOne(methodName.replaceAll("set", ""));
                String isParameterName = StringUtils.toLowerCaseFirstOne(methodName.replaceAll("is", ""));

                Class<?>[] types = method.getParameterTypes();
                if (param.containsKey(setParameterName)){
                    Object localParam = getInstance(types[0],param.get(setParameterName));
                    try {
                        method.invoke(instance,localParam);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }else if (param.containsKey(isParameterName)&&!setParameterName.equals(isParameterName)){
                    Object localParam = getInstance(types[0],param.get(isParameterName));
                    try {
                        method.invoke(instance,localParam);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 将request中读取的String类型的值转换成classSet中定义的数值类型的值
     * String -> Integer Double...
     * @param parameterType 转换的目标类型
     * @param value 需要转换的字符串
     * @return
     */
    private static Object getInstance(Class<?> parameterType, String value) {
        Object object = null;
        try {
            //通过反射 从类对象中获取参数为一个String的构造方法
            //Interger(String); Double(String) Boolean(String)...
            Constructor<?> constructor = parameterType.getConstructor(String.class);
            //通过构造方法new一个对象
            object = constructor.newInstance(value);
        }catch (Exception e){
            e.printStackTrace();
        }
        return object;
    }

    /**
     * 读取request请求 获取提供的参数列表
     * @param request
     * @return
     */
    private static Map<String, String> getParameter(HttpServletRequest request) {
        Map<String,String> result = new HashMap<>();
        /**
         * key1 = val1
         * key2 = val2
         * key2 = val3
         */
        Map<String, String[]> parameterMap = request.getParameterMap();

        for (String key : parameterMap.keySet()){
            String[] values = parameterMap.get(key);
            for (int i = 0;i<values.length;i++){
                result.put(key,values[i]);
            }
        }

        return result;
    }

}
